
namespace Math
{


template <typename T, typename K>
Matrix<T,K>::Matrix(const Matrix<T,K>& copy)
  {
      column_space = nullptr;

      lines_space = new Vector<T,K>*[4];
      copy.CopyLinesSpace(lines_space);
      CreateColumnSpace();
  }



template <typename T, typename K>
Matrix<T,K>::Matrix(Matrix<T,K>&& move)
  {
      column_space = move.column_space; 
      lines_space  = move.lines_space;

      move.column_space = nullptr;
      move.lines_space  = nullptr;
  }



template <typename T, typename K>
Matrix<T,K>::Matrix()
  {
      CreateLinesSpace((T*)zero_matrixd);
      CreateColumnSpace();
  }



template <typename T, typename K>
Matrix<T,K>::Matrix(const T* const matrix)
  {
      CreateLinesSpace(matrix);
      CreateColumnSpace();
  }


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////// Functions declration ///////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


template <typename T, typename K>
void Matrix<T,K>::MT()
  {
      Vector<T,K>** help_pointer = lines_space;

      lines_space  = column_space;
      column_space = help_pointer;
  }


template <typename T, typename K>
void Matrix<T,K>::LoadIdentityMatrix()
  {
      (*lines_space[0])[0] = static_cast<T>(1);
      (*lines_space[1])[1] = static_cast<T>(1);
      (*lines_space[2])[2] = static_cast<T>(1);
      (*lines_space[3])[3] = static_cast<T>(1);
  }


template <typename T, typename K>
void Matrix<T,K>::LoadZeroMatrix()
  {
      lines_space[0]->LoadZeroVector();
      lines_space[1]->LoadZeroVector();
      lines_space[2]->LoadZeroVector();
      lines_space[3]->LoadZeroVector();
  }


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////// operators //////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

template <typename T, typename K>
const Matrix<T,K>& Matrix<T,K>::operator=(Matrix<T,K>&& right)
  {
      DeleteSpace();

      lines_space  = right.lines_space;
      column_space = right.column_space;

      right.lines_space  = nullptr;
      right.column_space = nullptr;

      return *this;
  }



template <typename T, typename K>
const Matrix<T,K>& Matrix<T,K>::operator=(const Matrix<T,K>& right)
  {
      right.CopyLinesSpace(lines_space);
      right.CopyColumnSpace(column_space);

      return *this;
  }



template <typename T, typename K>
Vector<T,K>& Matrix<T,K>::operator[](ushorti index)
  {
      if(index >= 4) throw E_Math::IndexIsTooHighMatrix<T,K>(this, "Matrix<T,K>& Matrix<T,K>::operator[](XYZW)");

      return *(lines_space[index]);
  }



template <typename T, typename K>
const Vector<T,K>& Matrix<T,K>::operator[](ushorti index) const
  {
      if(index >= 4) throw E_Math::IndexIsTooHighMatrix<T,K>(this, "Matrix<T,K>& Matrix<T,K>::operator[](XYZW)");

      return *(lines_space[index]);
  }



template <typename T, typename K>
Matrix<T,K> Matrix<T,K>::operator*(const K& right) const
  {
      Matrix<T,K> help_matrix(*this);

      int i;
      for(i = 0; i < 4; i++) help_matrix = (help_matrix[i])*right;
      
      return help_matrix;
  }



template <typename T, typename K>
Matrix<T,K> Matrix<T,K>::operator*(const Matrix<T,K>& right) const
  {
      Matrix<T,K> help_matrix;
      Vector<T,K>** help_column = help_matrix.Getcolumn_space();

      ushorti i;
      for(i = 0; i < 4; i++) (*help_column[i]) = OWVector<T,K>::LinearCombination(column_space, *((right.Getcolumn_space())[i]));


      return help_matrix;
  }



template <typename T, typename K>
Matrix<T,K> Matrix<T,K>::operator+(const Matrix<T,K>& right) const
  {
      Matrix<T,K> help_matrix;

      ushorti i;
      for(i = 0; i < 4; i++) help_matrix[i] = (*lines_space[i]) + right[i];

      return help_matrix;
  }


/////////////////////////////////////////////// Friend operators //////////////////////////////////////////////////////////


template <typename T, typename K>
Matrix<T,K> operator*(const K& left, const Matrix<T,K>& right)
  {
      Matrix<T,K> help_matrix;

      ushorti i;
      for(i = 0; i < 4; i++) help_matrix[i] = left * right[i];

      return help_matrix;
  }



template <typename T, typename K>
ostream& operator<<(ostream& output, const Matrix<T,K>& right)
  {
      Vector<T,K>** help_pointer = right.Getcolumn_space();

      ushorti i;
      for(i = 0; i < 4; i++) cout << (*help_pointer[i]) << "\n";
      
      return output;
  }


//////////////////////////////////////////////// protected functions /////////////////////////////////////////////////////


template <typename T, typename K>
void Matrix<T,K>::CreateColumnSpace()
  {
      column_space     = new Vector<T,K>*[4];
      Vector<T,K>** ls = lines_space;

      ushorti i;
      for(i = 0; i < 4; i++) column_space[i] = new Vector<T,K>(&(*ls[X])[i],
                                                               &(*ls[Y])[i],
                                                               &(*ls[Z])[i],
                                                               &(*ls[W])[i], RC::REF);
  }



template <typename T, typename K>
void Matrix<T,K>::CreateLinesSpace(const T* const matrix)
  {
      lines_space = new Vector<T,K>*[4];

      ushorti i;
      for (i = 0; i < 4; i++) lines_space[i] = new Vector<T,K>(matrix + 4*i);
  }



template <typename T, typename K>
void Matrix<T,K>::CopyColumnSpace(Vector<T,K>** space) const
  {
      ushorti i;
      for(i = 0; i < 4; i++) (*space[i]) = (*column_space[i]);
  }



template <typename T, typename K>
void Matrix<T,K>::CopyLinesSpace(Vector<T,K>** space) const
  {
      ushorti i;
      for(i = 0; i < 4; i++) (*space[i]) = (*lines_space[i]);
  }



template <typename T, typename K>
void Matrix<T,K>::DeleteSpace()
  {
      if(lines_space != nullptr && column_space != nullptr)
      {
          ushorti i;
          for(i = 0; i < 4; i++)
          {
              delete lines_space[i];
              delete column_space[i];
          }

          delete[] lines_space;
          delete[] column_space;
      }
  }



} // end namespace Math
